/**
 *  CodeCompiler.java : IceBird project
 *  19:10:21 
 */
package icebird.compiler.ncomp;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import com.sun.org.apache.bcel.internal.classfile.ConstantLong;

import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import icebird.compiler.bytecode.BytecodeVisitor;
import icebird.compiler.ncomp.X86Register.GPR;
import icebird.compiler.ncomp.vs.*;
import icebird.metadata.*;
import icebird.metadata.Class;
import icebird.metadata.constpool.*;

/**
 * Default code compiler.
 * 
 * @author Sergey Shulepoff[Knott]
 */
public final class CodeCompiler extends BytecodeVisitor {

	private X86RegisterPool pool;
	private X86Assembler asm;

	private VirtualStack stack;

	private static CodeCompiler code;

	private Map<Integer, Item> constLocals;

	private StackFrame stackFrame;
	private FPCompiler fp;

	private int current;

	private Method source;

	private ArrayList<Label> extrns;

	public CodeCompiler() {
		pool = new X86RegisterPool();
		asm = new X86TextAssembler();

		stack = new VirtualStack();

		code = this;

		fp = null;
		if (fp == null)
			fp = null;

		constLocals = new HashMap<Integer, Item>();
		extrns = new ArrayList<Label>();
	}

	/**
	 * Get externs.
	 * 
	 * @return ArrayList<Label>
	 */
	public ArrayList<Label> getExterns() {
		return extrns;
	}
	
	public GPR request() {
		return request(BasicType.Int);
	}

	/**
	 * Request one register.
	 * 
	 * @return X86Register
	 */
	public GPR request(BasicType type) {
		return request(type, true);
	}
	
	public GPR request(BasicType type, boolean force) {
		X86Register reg = pool.request(type);
		if (reg == null && force) {
			stack.push();
			reg = pool.request(type);
		}

		return (GPR) reg;
	}

	public void prepareGPR(int count) {
		if (pool.unused() < count) {
			int delta = count - pool.unused();

			stack.free(delta);
		}
	}

	/**
	 * Gets code.
	 * 
	 * @return CodeCompiler
	 */
	public static CodeCompiler getCompiler() {
		return code;
	}

	/**
	 * Gets x86 assembler.
	 * 
	 * @return X86Assembler
	 */
	public X86Assembler getAssembler() {
		return asm;
	}

	/**
	 * Gets register pool.
	 * 
	 * @return X86RegisterPool
	 */
	public X86RegisterPool getPool() {
		return pool;
	}

	/**
	 * Returns virtual stack.
	 * 
	 * @return VirtualStack
	 */
	public VirtualStack getVS() {
		return stack;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#endInstruction()
	 */
	@Override
	public void endInstruction() {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#endMethod()
	 */
	@Override
	public void endMethod() {
		stackFrame.emitTrailer();

		asm.setMethodArea(false);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#startInstruction(int,
	 *      byte)
	 */
	@Override
	public void startInstruction(int pc, byte opcode) {
		current = pc;

		Label l = new Label(formatPC(pc));
		asm.bind(l);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#startMethod(icebird.metadata.Method)
	 */
	@Override
	public boolean startMethod(Method method) {
		stackFrame = new StackFrame(method);
		asm.header(method);

		Label l = new Label(method.getMangledName());
		asm.bind(l);

		asm.setMethodArea(true);

		stackFrame.emitHeader();

		source = method;

		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_aaload()
	 */
	@Override
	public void visit_aaload() {
		waload(new ReferenceType(null));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_aastore()
	 */
	@Override
	public void visit_aastore() {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_aconst_null()
	 */
	@Override
	public void visit_aconst_null() {
		stack.push(new RefItem(new NullConstantLocation()));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_aload(int)
	 */
	@Override
	public void visit_aload(int index) {
		wload(new ReferenceType(null), index);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_anewarray(icebird.metadata.constpool.ConstClass)
	 */
	@Override
	public void visit_anewarray(ConstClass clazz) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_areturn()
	 */
	@Override
	public void visit_areturn() {
		wreturn(new ReferenceType(null), true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_arraylength()
	 */
	@Override
	public void visit_arraylength() {
		RefItem r0 = stack.popRef();
		checknull(r0);

		prepareGPR(2);
		r0.load();

		GPR gpr = ((GPRLocation) r0.getLocation()).getGPR();
		GPR tmp = request(BasicType.Int);
		asm.move(tmp, gpr, 4);

		r0.release();

		stack.push(new IntegerItem(new GPRLocation(tmp)));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_astore(int)
	 */
	@Override
	public void visit_astore(int index) {
		wstore(new ReferenceType(null), index);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_athrow()
	 */
	@Override
	public void visit_athrow() {
		// TODO Auto-generated method stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_baload()
	 */
	@Override
	public void visit_baload() {
		waload(BasicType.Byte);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_bastore()
	 */
	@Override
	public void visit_bastore() {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_caload()
	 */
	@Override
	public void visit_caload() {
		waload(BasicType.Char);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_castore()
	 */
	@Override
	public void visit_castore() {
		// TODO: Loading
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_checkcast(icebird.metadata.constpool.ConstClass)
	 */
	@Override
	public void visit_checkcast(ConstClass clazz) {
		// TODO Auto-generated method stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_d2f()
	 */
	@Override
	public void visit_d2f() {
		fp.convert(BasicType.Double, BasicType.Float);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_d2i()
	 */
	@Override
	public void visit_d2i() {
		fp.convert(BasicType.Double, BasicType.Int);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_d2l()
	 */
	@Override
	public void visit_d2l() {
		fp.convert(BasicType.Double, BasicType.Long);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dadd()
	 */
	@Override
	public void visit_dadd() {
		fp.add(BasicType.Double);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_daload()
	 */
	@Override
	public void visit_daload() {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dastore()
	 */
	@Override
	public void visit_dastore() {
		// TODO Auto-generated method stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dcmpg()
	 */
	@Override
	public void visit_dcmpg() {
		fp.compare(true, BasicType.Double);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dcmpl()
	 */
	@Override
	public void visit_dcmpl() {
		fp.compare(false, BasicType.Double);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dconst(double)
	 */
	@Override
	public void visit_dconst(double value) {
		stack.push(new DoubleItem(new DoubleConstantLocation(value)));

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ddiv()
	 */
	@Override
	public void visit_ddiv() {
		fp.div(BasicType.Double);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dload(int)
	 */
	@Override
	public void visit_dload(int index) {
		dwload(BasicType.Double, index);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dmul()
	 */
	@Override
	public void visit_dmul() {
		fp.mul(BasicType.Double);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dneg()
	 */
	@Override
	public void visit_dneg() {
		fp.neg(BasicType.Double);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_drem()
	 */
	@Override
	public void visit_drem() {
		fp.rem(BasicType.Double);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dreturn()
	 */
	@Override
	public void visit_dreturn() {
		dwreturn(BasicType.Double, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dstore(int)
	 */
	@Override
	public void visit_dstore(int index) {
		dwstore(BasicType.Double, index);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dsub()
	 */
	@Override
	public void visit_dsub() {
		fp.sub(BasicType.Double);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dup()
	 */
	@Override
	public void visit_dup() {
		// Suitable only for one word item.
		Item i = stack.pop();
		Location l = i.getLocation();

		if (l.isFPUStack() || l.isStack()) {
			i.load();
		}

		stack.push(i);
		stack.push(i.clone());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dup2()
	 */
	@Override
	public void visit_dup2() {
		Item i0 = stack.pop();
		if (i0.getLocation().isFPUStack() || i0.getLocation().isStack()) {
			i0.load();
		}

		if (i0.getCategory() == 1) {
			Item i1 = stack.pop();
			if (i1.getLocation().isFPUStack() || i1.getLocation().isStack()) {
				i1.load();
			}

			stack.push(i1.clone());
			stack.push(i0.clone());
			stack.push(i1);
			stack.push(i0);
		} else {
			stack.push(i0.clone());
			stack.push(i0);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dup2_x1()
	 */
	@Override
	public void visit_dup2_x1() {
		Item i0 = stack.pop();
		Item i1 = stack.pop();

		if (i0.getLocation().isFPUStack() || i0.getLocation().isStack()) {
			i0.load();
		}

		if (i1.getLocation().isFPUStack() || i1.getLocation().isStack()) {
			i1.load();
		}

		if (i0.getCategory() == 2) {
			stack.push(i0.clone());
			stack.push(i1);
			stack.push(i0);
		} else {
			Item i2 = stack.pop();
			if (i2.getLocation().isFPUStack() || i2.getLocation().isStack()) {
				i2.load();
			}

			stack.push(i1.clone());
			stack.push(i0.clone());
			stack.push(i2);
			stack.push(i1);
			stack.push(i0);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dup2_x2()
	 */
	@Override
	public void visit_dup2_x2() {
		System.out.println("icebird: dup2_x2 instruction not implemented.");
		throw new NotImplementedException();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dup_x1()
	 */
	@Override
	public void visit_dup_x1() {
		Item i0 = stack.pop();
		Item i1 = stack.pop();

		if (i0.getLocation().isFPUStack() || i0.getLocation().isStack()) {
			i0.load();
		}

		if (i1.getLocation().isFPUStack() || i1.getLocation().isStack()) {
			i1.load();
		}

		stack.push(i0.clone());
		stack.push(i1);
		stack.push(i0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_dup_x2()
	 */
	@Override
	public void visit_dup_x2() {
		Item i0 = stack.pop();
		Item i1 = stack.pop();

		if (i0.getLocation().isFPUStack() || i0.getLocation().isStack()) {
			i0.load();
		}

		if (i1.getLocation().isFPUStack() || i1.getLocation().isStack()) {
			i1.load();
		}

		if (i1.getCategory() == 2) {
			stack.push(i0.clone());
			stack.push(i1);
			stack.push(i0);
		} else {
			Item i2 = stack.pop();
			if (i2.getLocation().isFPUStack() || i2.getLocation().isStack()) {
				i2.load();
			}

			stack.push(i0.clone());
			stack.push(i2);
			stack.push(i1);
			stack.push(i0);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_f2d()
	 */
	@Override
	public void visit_f2d() {
		fp.convert(BasicType.Float, BasicType.Double);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_f2i()
	 */
	@Override
	public void visit_f2i() {
		fp.convert(BasicType.Float, BasicType.Int);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_f2l()
	 */
	@Override
	public void visit_f2l() {
		fp.convert(BasicType.Float, BasicType.Long);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_fadd()
	 */
	@Override
	public void visit_fadd() {
		fp.add(BasicType.Float);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_faload()
	 */
	@Override
	public void visit_faload() {
		fp.fpaload(BasicType.Float);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_fastore()
	 */
	@Override
	public void visit_fastore() {
		// TODO Auto-generated method stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_fcmpg()
	 */
	@Override
	public void visit_fcmpg() {
		fp.compare(false, BasicType.Float);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_fcmpl()
	 */
	@Override
	public void visit_fcmpl() {
		fp.compare(true, BasicType.Float);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_fconst(float)
	 */
	@Override
	public void visit_fconst(float value) {
		stack.push(new FloatItem(new FloatConstantLocation(value)));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_fdiv()
	 */
	@Override
	public void visit_fdiv() {
		fp.div(BasicType.Float);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_fload(int)
	 */
	@Override
	public void visit_fload(int index) {
		wload(BasicType.Float, index);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_fmul()
	 */
	@Override
	public void visit_fmul() {
		fp.mul(BasicType.Float);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_fneg()
	 */
	@Override
	public void visit_fneg() {
		fp.neg(BasicType.Float);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_frem()
	 */
	@Override
	public void visit_frem() {
		fp.rem(BasicType.Float);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_freturn()
	 */
	@Override
	public void visit_freturn() {
		wreturn(BasicType.Float, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_fstore(int)
	 */
	@Override
	public void visit_fstore(int index) {
		wstore(BasicType.Float, index);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_fsub()
	 */
	@Override
	public void visit_fsub() {
		fp.sub(BasicType.Float);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_getfield(icebird.metadata.constpool.ConstField)
	 */
	@Override
	public void visit_getfield(ConstField fieldRef) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_getstatic(icebird.metadata.constpool.ConstField)
	 */
	@Override
	public void visit_getstatic(ConstField fieldRef) {
		StaticField field = (StaticField) fieldRef.resolve();

		if (field.getDeclaringType().isConstantField(field)) {
			ConstLocation l = (ConstLocation) field.getDeclaringType()
					.getConstFields().get(field);

			stack.push(Item.getByType(field.getType(), l));
		} else {
			// TODO: Implement field getting.
			extern(field);

			if (field.isWide()) {
				prepareGPR(2);

				GPR r0 = request(BasicType.Int);
				GPR r1 = request(BasicType.Int);

				GPR2Location loc = new GPR2Location(r0, r1);
				asm.move(r0, new Label(field.getMangledName()), 0);
				asm.move(r1, new Label(field.getMangledName()), 4);

				stack.push(DoubleWordItem.getByType(field.getType(), loc));
			} else {
				GPR gpr = request(field.getType());
				asm.move(gpr, new Label(field.getMangledName()), 0);

				stack.push(WordItem.getByType(field.getType(), new GPRLocation(
						gpr)));
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_goto(int)
	 */
	@Override
	public void visit_goto(int address) {
		Label jmp = new Label(formatPC(address));
		asm.jmp(jmp);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_i2b()
	 */
	@Override
	public void visit_i2b() {
		IntegerItem v0 = stack.popInteger();

		if (v0.getLocation().isConstant()) {
			IntegerConstantLocation c = (IntegerConstantLocation) v0
					.getLocation();
			stack.push(new IntegerItem(new IntegerConstantLocation(
					c.getValue() & 0xFF)));
			return;
		}

		// Load to GPR
		v0.load();

		asm.and(((GPRLocation) v0.getLocation()).getGPR(), 0x000000FF);

		// Push into stack v0.
		stack.push(v0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_i2c()
	 */
	@Override
	public void visit_i2c() {
		IntegerItem v0 = stack.popInteger();

		// Compiler optimization.
		if (v0.getLocation().isConstant()) {
			IntegerConstantLocation c = (IntegerConstantLocation) v0
					.getLocation();
			stack.push(new IntegerItem(new IntegerConstantLocation(
					c.getValue() & 0xFFFF)));
			return;
		}

		// Load to GPR
		v0.load();

		asm.and(((GPRLocation) v0.getLocation()).getGPR(), 0x0000FFFF);

		// Push into stack v0.
		stack.push(v0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_i2d()
	 */
	@Override
	public void visit_i2d() {
		fp.convert(BasicType.Int, BasicType.Double);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_i2f()
	 */
	@Override
	public void visit_i2f() {
		fp.convert(BasicType.Int, BasicType.Float);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_i2l()
	 */
	@Override
	public void visit_i2l() {
		// TODO Auto-generated method stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_i2s()
	 */
	@Override
	public void visit_i2s() {
		IntegerItem v0 = stack.popInteger();

		if (v0.getLocation().isConstant()) {
			IntegerConstantLocation c = (IntegerConstantLocation) v0
					.getLocation();
			stack.push(new IntegerItem(new IntegerConstantLocation(
					c.getValue() & 0xFFFF)));
			return;
		}

		// Load to GPR
		v0.load();

		asm.and(((GPRLocation) v0.getLocation()).getGPR(), 0x0000FFFF);

		// Push into stack v0.
		stack.push(v0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_iadd()
	 */
	@Override
	public void visit_iadd() {
		iop(X86Operation.ADD, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_iaload()
	 */
	@Override
	public void visit_iaload() {
		waload(BasicType.Int);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_iand()
	 */
	@Override
	public void visit_iand() {
		iop(X86Operation.AND, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_iastore()
	 */
	@Override
	public void visit_iastore() {
		// TODO Auto-generated method stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_iconst(int)
	 */
	@Override
	public void visit_iconst(int value) {
		stack.push(new IntegerItem(new IntegerConstantLocation(value)));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_idiv()
	 */
	@Override
	public void visit_idiv() {
		IntegerItem v2 = stack.popInteger();
		IntegerItem v1 = stack.popInteger();

		if (isConstant(v2)) {

			// Both constant - make compile time
			// operation.
			if (isConstant(v1)) {
				visit_iconst(((IntegerConstantLocation) v1.getLocation())
						.getValue()
						/ ((IntegerConstantLocation) v2.getLocation())
								.getValue());
			} else {
				int value = ((IntegerConstantLocation) v2.getLocation())
						.getValue();
				int shift = getShiftForMultiplier(value);

				if (shift > 0) {
					v1.load();
					asm.sar(((GPRLocation) v1.getLocation()).getGPR(), shift);

					stack.push(v1);
				} else {
					boolean veax = stack.uses(GPR.EAX);
					if (veax) {
						asm.push(GPR.EAX);
					}

					v2.load();
					v1.loadTo(X86Register.EAX);

					asm.idiv(((GPRLocation) v2.getLocation()).getGPR());

					stack.push(v1);

					if (veax) {
						asm.pop(GPR.EAX);
					}
				}
			}

			v2.release();
		} else {
			boolean veax = stack.uses(GPR.EAX);
			if (veax) {
				asm.push(GPR.EAX);
			}

			v2.load();
			v1.loadTo(X86Register.EAX);

			asm.idiv(((GPRLocation) v2.getLocation()).getGPR());

			stack.push(v1);
			v2.release();

			if (veax) {
				asm.pop(GPR.EAX);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_if_acmpeq(int)
	 */
	@Override
	public void visit_if_acmpeq(int address) {
		RefItem a1 = stack.popRef();
		RefItem a0 = stack.popRef();

		Label jmp = new Label(formatPC(address));

		prepareGPR(2);

		a1.load();
		a0.load();

		GPR ar1 = ((GPRLocation) a1.getLocation()).getGPR();
		GPR ar0 = ((GPRLocation) a0.getLocation()).getGPR();

		asm.cmp(ar0, ar1);
		asm.jcc(jmp, X86Constants.JE);

		a1.release();
		a0.release();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_if_acmpne(int)
	 */
	@Override
	public void visit_if_acmpne(int address) {
		RefItem a1 = stack.popRef();
		RefItem a0 = stack.popRef();

		Label jmp = new Label(formatPC(address));

		prepareGPR(2);

		a1.load();
		a0.load();

		GPR ar1 = ((GPRLocation) a1.getLocation()).getGPR();
		GPR ar0 = ((GPRLocation) a0.getLocation()).getGPR();

		asm.cmp(ar0, ar1);
		asm.jcc(jmp, X86Constants.JNE);

		a1.release();
		a0.release();
	}

	private void icmpcc(int address, int cc) {
		IntegerItem a1 = stack.popInteger();
		IntegerItem a0 = stack.popInteger();

		Label jmp = new Label(formatPC(address));
		
		if (isConstant(a0, a1)) {
			int va1 = ((IntegerConstantLocation)a1.getLocation()).getValue();
			int va0 = ((IntegerConstantLocation)a0.getLocation()).getValue();
			
			switch (cc) {
			case X86Constants.JE:
				if (va1 == va0) asm.jmp(jmp);
				break;
				
			case X86Constants.JGE:
				if (va1 >= va0) asm.jmp(jmp);
				break;
				
			case X86Constants.JG:
				if (va1 > va0) asm.jmp(jmp);
				break;
				
			case X86Constants.JLE:
				if (va1 <= va0) asm.jmp(jmp);
				break;
				
			case X86Constants.JL:
				if (va1 < va0) asm.jmp(jmp);
				break;
				
			case X86Constants.JNE:
				if (va1 != va0) asm.jmp(jmp);
				break;
				
			default:
				throw new RuntimeException();
			}
		} else {
			prepareGPR(2);

			a1.load();
			a0.load();

			GPR ar1 = ((GPRLocation) a1.getLocation()).getGPR();
			GPR ar0 = ((GPRLocation) a0.getLocation()).getGPR();

			asm.cmp(ar0, ar1);
			asm.jcc(jmp, cc);

			a1.release();
			a0.release();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_if_icmpeq(int)
	 */
	@Override
	public void visit_if_icmpeq(int address) {
		icmpcc(address, X86Constants.JE);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_if_icmpge(int)
	 */
	@Override
	public void visit_if_icmpge(int address) {
		icmpcc(address, X86Constants.JGE);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_if_icmpgt(int)
	 */
	@Override
	public void visit_if_icmpgt(int address) {
		icmpcc(address, X86Constants.JG);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_if_icmple(int)
	 */
	@Override
	public void visit_if_icmple(int address) {
		icmpcc(address, X86Constants.JLE);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_if_icmplt(int)
	 */
	@Override
	public void visit_if_icmplt(int address) {
		icmpcc(address, X86Constants.JL);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_if_icmpne(int)
	 */
	@Override
	public void visit_if_icmpne(int address) {
		icmpcc(address, X86Constants.JNE);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ifeq(int)
	 */
	@Override
	public void visit_ifeq(int address) {
		IntegerItem v = stack.popInteger();
		Label jmp = new Label(formatPC(address));

		if (v.isPureVirtual()) {
			int i = ((IntegerConstantLocation) v.getLocation()).getValue();
			if (i == 0) {
				asm.jmp(jmp);
			}
		} else {
			v.load();

			GPR gpr = ((GPRLocation) v.getLocation()).getGPR();
			asm.cmp(gpr, 0);

			asm.jcc(jmp, X86Constants.JE);

			v.release();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ifge(int)
	 */
	@Override
	public void visit_ifge(int address) {
		IntegerItem v = stack.popInteger();
		Label jmp = new Label(formatPC(address));

		if (v.isPureVirtual()) {
			int i = ((IntegerConstantLocation) v.getLocation()).getValue();
			if (i >= 0) {
				asm.jmp(jmp);
			}
		} else {
			v.load();

			GPR gpr = ((GPRLocation) v.getLocation()).getGPR();
			asm.cmp(gpr, 0);

			asm.jcc(jmp, X86Constants.JGE);

			v.release();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ifgt(int)
	 */
	@Override
	public void visit_ifgt(int address) {
		IntegerItem v = stack.popInteger();
		Label jmp = new Label(formatPC(address));

		if (v.isPureVirtual()) {
			int i = ((IntegerConstantLocation) v.getLocation()).getValue();
			if (i > 0) {
				asm.jmp(jmp);
			}
		} else {
			v.load();

			GPR gpr = ((GPRLocation) v.getLocation()).getGPR();
			asm.cmp(gpr, 0);

			asm.jcc(jmp, X86Constants.JG);

			v.release();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ifle(int)
	 */
	@Override
	public void visit_ifle(int address) {
		IntegerItem v = stack.popInteger();
		Label jmp = new Label(formatPC(address));

		if (v.isPureVirtual()) {
			int i = ((IntegerConstantLocation) v.getLocation()).getValue();
			if (i <= 0) {
				asm.jmp(jmp);
			}
		} else {
			v.load();

			GPR gpr = ((GPRLocation) v.getLocation()).getGPR();
			asm.cmp(gpr, 0);

			asm.jcc(jmp, X86Constants.JLE);

			v.release();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_iflt(int)
	 */
	@Override
	public void visit_iflt(int address) {
		IntegerItem v = stack.popInteger();
		Label jmp = new Label(formatPC(address));

		if (v.isPureVirtual()) {
			int i = ((IntegerConstantLocation) v.getLocation()).getValue();
			if (i < 0) {
				asm.jmp(jmp);
			}
		} else {
			v.load();

			GPR gpr = ((GPRLocation) v.getLocation()).getGPR();
			asm.cmp(gpr, 0);

			asm.jcc(jmp, X86Constants.JL);

			v.release();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ifne(int)
	 */
	@Override
	public void visit_ifne(int address) {
		IntegerItem v = stack.popInteger();
		Label jmp = new Label(formatPC(address));

		if (v.isPureVirtual()) {
			int i = ((IntegerConstantLocation) v.getLocation()).getValue();
			if (i != 0) {
				asm.jmp(jmp);
			}
		} else {
			v.load();

			GPR gpr = ((GPRLocation) v.getLocation()).getGPR();
			asm.cmp(gpr, 0);

			asm.jcc(jmp, X86Constants.JNE);

			v.release();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ifnonnull(int)
	 */
	@Override
	public void visit_ifnonnull(int address) {
		RefItem v = stack.popRef();
		Label jmp = new Label(formatPC(address));

		if (v.isPureVirtual()) {
			// Constant - only null.
			// ignore
		} else {
			v.load();

			GPR gpr = ((GPRLocation) v.getLocation()).getGPR();
			asm.cmp(gpr, 0);
			asm.jcc(jmp, X86Constants.JNE);

			v.release();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ifnull(int)
	 */
	@Override
	public void visit_ifnull(int address) {
		RefItem v = stack.popRef();
		Label jmp = new Label(formatPC(address));

		if (v.isPureVirtual()) {
			// Constant - only null.
			asm.jmp(jmp);
		} else {
			v.load();

			GPR gpr = ((GPRLocation) v.getLocation()).getGPR();
			asm.cmp(gpr, 0);
			asm.jcc(jmp, X86Constants.JE);

			v.release();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_iinc(int, int)
	 */
	@Override
	public void visit_iinc(int index, int incValue) {
		if (constLocals.containsKey(index)) {
			IntegerItem item = (IntegerItem)constLocals.get(index);
			int value = ((IntegerConstantLocation)item.getLocation()).getValue();
			
			item.setLocation(ConstLocation.createIConst(value + incValue));
		} else {
			int offset = getOffset(index);

			if (incValue == 0)
				return;

			if (incValue == 1) {
				asm.inc(GPR.EBP, offset);
			} else if (incValue == -1) {
				asm.dec(GPR.EBP, offset);
			} else {
				GPR acumulator = request(BasicType.Int, false);
				boolean violatile = false;
				
				if (acumulator == null) {
					violatile = true;
					acumulator = GPR.EAX;
				}
				
				if (violatile) {
					asm.push(acumulator);
				}
				
				asm.move(acumulator, GPR.EBP, offset);
				if (incValue < 0) {
					asm.sub(acumulator, incValue);
				} else {
					asm.add(acumulator, incValue);
				}
				asm.move(GPR.EBP, offset, acumulator);
				
				if (violatile) {
					asm.pop(acumulator);
				}
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_iload(int)
	 */
	@Override
	public void visit_iload(int index) {
		wload(BasicType.Int, index);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_imul()
	 */
	@Override
	public void visit_imul() {
		IntegerItem v2 = stack.popInteger();
		IntegerItem v1 = stack.popInteger();

		if (isConstant(v1, v2)) {
			visit_iconst(((IntegerConstantLocation) v1.getLocation())
					.getValue()
					* ((IntegerConstantLocation) v2.getLocation()).getValue());
		} else {
			if (prepare(v1, v2, true)) {
				// (Un)Swap
				IntegerItem tmp = v2;
				v2 = v1;
				v1 = tmp;
			}

			Location l = v2.getLocation();
			GPR r1 = ((GPRLocation) v1.getLocation()).getGPR();

			if (l.isGPR()) {
				asm.imul(r1, ((GPRLocation) l).getGPR());
			}

			if (l.isConstant()) {
				int value = ((IntegerConstantLocation) l).getValue();

				if (value == 0) {
					asm.clear(r1); // x * 0 = 0
				} else if (value == -1) {
					asm.neg(r1); // x * -1 = -x
				} else {
					int shift = getShiftForMultiplier(Math.abs(value));

					if (shift > 0) {
						asm.sal(r1, shift);
						if (value < 0) {
							asm.neg(r1);
						}
					} else {
						asm.imul(r1, r1, value);
					}
				}
			}

			v2.release();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ineg()
	 */
	@Override
	public void visit_ineg() {
		IntegerItem i = stack.popInteger();

		if (i.getLocation().isConstant()) {
			visit_iconst(-((IntegerConstantLocation) i.getLocation())
					.getValue());
		} else {
			i.load();
			asm.neg(((GPRLocation) i.getLocation()).getGPR());

			stack.push(i);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_instanceof(icebird.metadata.constpool.ConstClass)
	 */
	@Override
	public void visit_instanceof(ConstClass clazz) {
		RefItem i = (RefItem) stack.pop();

		if (i.getType().getTypeName() != null) {
			// Push 1 into stack.
			stack.push(new IntegerItem(new IntegerConstantLocation(i.getType()
					.getTypeName().equals(clazz.getName()) ? 1 : 0)));
		} else {
			// TODO: Implement runtime type check.
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_invokeinterface(icebird.metadata.constpool.ConstIMethod,
	 *      int)
	 */
	@Override
	public void visit_invokeinterface(ConstIMethod methodRef, int count) {
		// TODO Auto-generated method stub
		throw new NotImplementedException();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_invokespecial(icebird.metadata.constpool.ConstMethod)
	 */
	@Override
	public void visit_invokespecial(ConstMethod methodRef) {
		SpecialMethod m = (SpecialMethod) methodRef.getConstClass()
				.getClassDef().getMethod(methodRef);
		if (m.isEmpty())
			return;

		stack.push();
		dropParameters(m, true);

		callMethod(m);

		// add to externs.
		extern(m);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_invokestatic(icebird.metadata.constpool.ConstMethod)
	 */
	@Override
	public void visit_invokestatic(ConstMethod methodRef) {
		StaticMethod m = (StaticMethod) methodRef.getConstClass().getClassDef()
				.getMethod(methodRef);
		if (m.isEmpty())
			return;

		stack.push();
		dropParameters(m, false);

		callMethod(m);

		// add to externs.
		extern(m);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_invokevirtual(icebird.metadata.constpool.ConstMethod)
	 */
	@Override
	public void visit_invokevirtual(ConstMethod methodRef) {
		InstanceMethod m = (InstanceMethod) methodRef.resolve();
		if (m.isEmpty())
			return;

		stack.push();
		dropParameters(m, true);

		if (m.isFinal() || m.isNative()) {
			callMethod(m);

			// add to externs.
			extern(m);
		} else {
			// TODO: Implement virtual method invokation.
			throw new NotImplementedException();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ior()
	 */
	@Override
	public void visit_ior() {
		iop(X86Operation.OR, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_irem()
	 */
	@Override
	public void visit_irem() {
		IntegerItem v0 = stack.popInteger();
		IntegerItem v1 = stack.popInteger();

		boolean veax = stack.uses(GPR.EAX);

		if (veax) {
			asm.push(GPR.EAX);
		}

		v0.loadTo(GPR.EAX);
		v1.load();

		GPR tmp = request(BasicType.Int);
		asm.move(tmp, GPR.EAX);

		GPR gpr = ((GPRLocation) v1.getLocation()).getGPR();
		asm.idiv(gpr);
		asm.imul(GPR.EAX, gpr);
		asm.sub(tmp, GPR.EAX);

		v0.release();
		v1.release();

		stack.push(new IntegerItem(new GPRLocation(tmp)));

		if (veax) {
			asm.pop(GPR.EAX);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ireturn()
	 */
	@Override
	public void visit_ireturn() {
		wreturn(BasicType.Int, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ishl()
	 */
	@Override
	public void visit_ishl() {
		IntegerItem value = stack.popInteger();
		IntegerItem amount = stack.popInteger();

		if (amount.isPureVirtual()) {
			value.load();
			GPR gpr = ((GPRLocation) value.getLocation()).getGPR();

			asm.shl(gpr, ((IntegerConstantLocation) value.getLocation())
					.getValue());

			stack.push(value);
		} else {
			// If ECX used.
			boolean vecx = stack.uses(GPR.ECX);

			if (vecx) {
				// Backup ecx register, if used.
				asm.push(GPR.ECX);
			}

			value.load();
			GPR gpr = ((GPRLocation) value.getLocation()).getGPR();

			amount.loadTo(GPR.ECX);
			asm.shl(gpr, GPR.CL);

			if (vecx) {
				// Restore backuped ecx register, if used.
				asm.pop(GPR.ECX);
			}

			// amount.release(); // Oga, oga
			stack.push(value);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ishr()
	 */
	@Override
	public void visit_ishr() {
		IntegerItem value = stack.popInteger();
		IntegerItem amount = stack.popInteger();

		if (amount.isPureVirtual()) {
			value.load();
			GPR gpr = ((GPRLocation) value.getLocation()).getGPR();

			asm.shr(gpr, ((IntegerConstantLocation) value.getLocation())
					.getValue());

			stack.push(value);
		} else {
			// If ECX used.
			boolean vecx = stack.uses(GPR.ECX);

			if (vecx) {
				// Backup ecx register, if used.
				asm.push(GPR.ECX);
			}

			value.load();
			GPR gpr = ((GPRLocation) value.getLocation()).getGPR();

			amount.loadTo(GPR.ECX);
			asm.shr(gpr, GPR.CL);

			if (vecx) {
				// Restore backuped ecx register, if used.
				asm.pop(GPR.ECX);
			}

			// amount.release(); // Oga, oga
			stack.push(value);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_istore(int)
	 */
	@Override
	public void visit_istore(int index) {
		wstore(BasicType.Int, index);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_isub()
	 */
	@Override
	public void visit_isub() {
		iop(X86Operation.SUB, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_iushr()
	 */
	@Override
	public void visit_iushr() {
		System.out.println("icebird: iushr is not implemented.");
		throw new NotImplementedException();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ixor()
	 */
	@Override
	public void visit_ixor() {
		iop(X86Operation.XOR, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_jsr(int)
	 */
	@Override
	public void visit_jsr(int address, int ret) {
		Label rp = new Label(formatPC(ret));
		Label jp = new Label(formatPC(address));

		GPR gpr = request(BasicType.Int);
		asm.move(gpr, rp);

		IntegerItem retAddr = new IntegerItem(new GPRLocation(gpr));
		stack.push(retAddr);

		asm.jmp(jp);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_l2d()
	 */
	@Override
	public void visit_l2d() {
		fp.convert(BasicType.Long, BasicType.Double);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_l2f()
	 */
	@Override
	public void visit_l2f() {
		fp.convert(BasicType.Long, BasicType.Float);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_l2i()
	 */
	@Override
	public void visit_l2i() {
		// TODO Auto-generated method stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ladd()
	 */
	@Override
	public void visit_ladd() {
		lop(X86Operation.ADD, X86Operation.ADC, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_laload()
	 */
	@Override
	public void visit_laload() {
		// TODO Auto-generated method stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_land()
	 */
	@Override
	public void visit_land() {
		lop(X86Operation.AND, X86Operation.AND, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lastore()
	 */
	@Override
	public void visit_lastore() {
		// TODO Auto-generated method stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lcmp()
	 */
	@Override
	public void visit_lcmp() {
		LongItem v0 = stack.popLong();
		LongItem v1 = stack.popLong();

		if (v0.isPureVirtual() && v1.isPureVirtual()) {
			long vv0 = ((LongConstantLocation) v0.getLocation()).getValue();
			long vv1 = ((LongConstantLocation) v1.getLocation()).getValue();

			if (vv0 == vv1) {
				stack.push(new IntegerItem(ConstLocation.createIConst(0)));
			} else if (vv0 < vv1) {
				stack.push(new IntegerItem(ConstLocation.createIConst(1)));
			} else if (vv0 > vv1) {
				stack.push(new IntegerItem(ConstLocation.createIConst(-1)));
			}
		} else {
			v0.load();
			v1.load();

			GPR2Location vl0 = ((GPR2Location) v0.getLocation());
			GPR2Location vl1 = ((GPR2Location) v1.getLocation());

			Label secondCheck = new Label();
			Label less = new Label();
			Label greater = new Label();
			Label equal = new Label();
			Label done = new Label();

			asm.sub(vl0.getLW(), vl1.getLW());

			asm.test(vl0.getLW(), vl0.getLW());
			asm.jcc(secondCheck, X86Constants.JZ);

			asm.cmp(vl0.getLW(), 0);
			asm.jcc(less, X86Constants.JL);
			asm.jmp(greater);

			asm.bind(secondCheck);

			asm.sub(vl0.getHW(), vl1.getHW());

			asm.test(vl0.getHW(), vl0.getHW());
			asm.jcc(equal, X86Constants.JE);

			asm.cmp(vl0.getHW(), 0);
			asm.jcc(less, X86Constants.JL);
			asm.jmp(greater);

			v0.release();
			v1.release();

			GPR result = request(BasicType.Int);
			asm.bind(less);
			asm.move_const(result, -1);
			asm.jmp(done);

			asm.bind(greater);
			asm.move_const(result, 1);
			asm.jmp(done);

			asm.bind(equal);
			asm.move_const(result, 0);
			// asm.jmp(done); // pass throught.

			asm.bind(done);

			stack.push(new IntegerItem(new GPRLocation(result)));
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lconst(long)
	 */
	@Override
	public void visit_lconst(long value) {
		stack.push(new LongItem(new LongConstantLocation(value)));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ldc(java.lang.String)
	 */
	@Override
	public void visit_ldc(String value) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ldc(icebird.metadata.constpool.ConstClass)
	 */
	@Override
	public void visit_ldc(ConstClass value) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ldiv()
	 */
	@Override
	public void visit_ldiv() {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lload(int)
	 */
	@Override
	public void visit_lload(int index) {
		dwload(BasicType.Long, index);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lmul()
	 */
	@Override
	public void visit_lmul() {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lneg()
	 */
	@Override
	public void visit_lneg() {
		LongItem l = stack.popLong();

		if (l.isPureVirtual()) {
			long value = ((LongConstantLocation) l.getLocation()).getValue();
			stack.push(new LongItem(new LongConstantLocation(-value)));
		} else {
			l.load();
			GPR2Location loc = (GPR2Location) l.getLocation();

			GPR lsb = loc.getLW();
			GPR msb = loc.getHW();

			asm.neg(lsb);
			asm.neg(msb);

			asm.sbb(msb, 0);

			stack.push(l);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lookupswitch(int,
	 *      int[], int[])
	 */
	@Override
	public void visit_lookupswitch(int defValue, int[] matchValues,
			int[] addresses) {
		IntegerItem v = stack.popInteger();

		if (v.isPureVirtual()) {
			int value = ((IntegerConstantLocation) v.getLocation()).getValue();
			Label l = null;

			for (int i = 0; i < matchValues.length; i++) {
				if (matchValues[i] == value) {
					l = new Label(formatPC(addresses[i]));
				}
			}

			if (l == null) {
				l = new Label(formatPC(defValue));
			}

			asm.jmp(l);
		} else {
			v.load();

			GPR gpr = ((GPRLocation) v.getLocation()).getGPR();
			for (int i = 0; i < matchValues.length; i++) {
				asm.cmp(gpr, matchValues[i]);
				asm.jcc(new Label(formatPC(addresses[i])), X86Constants.JE);
			}

			v.release();

			asm.jmp(new Label(formatPC(defValue)));
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lor()
	 */
	@Override
	public void visit_lor() {
		lop(X86Operation.OR, X86Operation.OR, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lrem()
	 */
	@Override
	public void visit_lrem() {
		// TODO Auto-generated method stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lreturn()
	 */
	@Override
	public void visit_lreturn() {
		dwreturn(BasicType.Long, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lshl()
	 */
	@Override
	public void visit_lshl() {
		IntegerItem count = stack.popInteger();
		LongItem value = stack.popLong();

		boolean vecx = stack.uses(GPR.ECX);
		if (vecx) {
			asm.push(GPR.ECX);
		}

		count.loadTo(GPR.ECX);
		value.load();

		GPR2Location gpr2 = (GPR2Location) value.getLocation();

		GPR lsb = gpr2.getLW();
		GPR msb = gpr2.getHW();

		Label g32 = new Label();
		Label endLabel = new Label();

		asm.and(GPR.ECX, 63);
		asm.cmp(GPR.ECX, 32);

		asm.jcc(g32, X86Constants.JAE);

		asm.shld(msb, lsb);
		asm.shl(lsb, GPR.CL);
		asm.jmp(endLabel);

		asm.bind(g32);

		asm.move(msb, lsb);
		asm.clear(lsb);
		asm.shl(msb, GPR.CL);

		asm.bind(endLabel);

		if (vecx) {
			asm.pop(GPR.ECX);
		}

		count.release();
		stack.push(value);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lshr()
	 */
	@Override
	public void visit_lshr() {
		IntegerItem count = stack.popInteger();
		LongItem value = stack.popLong();

		boolean vecx = stack.uses(GPR.ECX);
		if (vecx) {
			asm.push(GPR.ECX);
		}

		count.loadTo(GPR.ECX);
		value.load();

		GPR2Location gpr2 = (GPR2Location) value.getLocation();

		GPR lsb = gpr2.getLW();
		GPR msb = gpr2.getHW();

		Label g32 = new Label();
		Label endLabel = new Label();

		asm.and(GPR.ECX, 63);
		asm.cmp(GPR.ECX, 32);

		asm.jcc(g32, X86Constants.JAE);

		asm.shrd(lsb, msb);
		asm.sar(msb, GPR.CL);
		asm.jmp(endLabel);

		asm.bind(g32);

		asm.move(msb, lsb);
		asm.sar(msb, 31);
		asm.sar(lsb, GPR.CL);

		asm.bind(endLabel);

		if (vecx) {
			asm.pop(GPR.ECX);
		}

		count.release();
		stack.push(value);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lstore(int)
	 */
	@Override
	public void visit_lstore(int index) {
		dwstore(BasicType.Long, index);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lsub()
	 */
	@Override
	public void visit_lsub() {
		lop(X86Operation.SUB, X86Operation.SBB, false);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lushr()
	 */
	@Override
	public void visit_lushr() {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_lxor()
	 */
	@Override
	public void visit_lxor() {
		lop(X86Operation.XOR, X86Operation.XOR, true);

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_monitorenter()
	 */
	@Override
	public void visit_monitorenter() {
		throw new NotImplementedException();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_monitorexit()
	 */
	@Override
	public void visit_monitorexit() {
		throw new NotImplementedException();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_multianewarray(icebird.metadata.constpool.ConstClass,
	 *      int)
	 */
	@Override
	public void visit_multianewarray(ConstClass clazz, int dimensions) {
		throw new NotImplementedException();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_new(icebird.metadata.constpool.ConstClass)
	 */
	@Override
	public void visit_new(ConstClass clazz) {
		throw new NotImplementedException();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_newarray(int)
	 */
	@Override
	public void visit_newarray(int type) {
		BasicType t;

		switch (type) {
		case 4:
			t = BasicType.Boolean;
			break;

		case 5:
			t = BasicType.Char;
			break;

		case 6:
			t = BasicType.Float;
			break;

		case 7:
			t = BasicType.Double;
			break;

		case 8:
			t = BasicType.Byte;
			break;

		case 9:
			t = BasicType.Short;
			break;

		case 10:
			t = BasicType.Int;
			break;

		case 11:
			t = BasicType.Long;
			break;

		default:
			throw new RuntimeException("Unknown type");
		}

		int elemSize = t.getSize();

		IntegerItem n = stack.popInteger();
		if (n.getLocation().isConstant()) {
			int size = ((IntegerConstantLocation) n.getLocation()).getValue()
					* elemSize;
			size += 4; // Header.

			boolean veax = stack.uses(GPR.EAX);
			if (veax) {
				asm.push(GPR.EAX);
			}

			// if (stack.uses(GPR.EAX)) {
			// stack.push(); // Clear eax
			// }

			asm.push(size);
			callInternal(gcalloc);

			stack.push(new RefItem(new GPRLocation(GPR.EAX)));

			asm.move_const(GPR.EAX, 0, ((IntegerConstantLocation) n
					.getLocation()).getValue());

			if (veax) {
				asm.pop(GPR.EAX);
			}
		} else {
			boolean veax = stack.uses(GPR.EAX);
			// if (stack.uses(GPR.EAX)) {
			// stack.push(); // Clear eax
			// }

			if (veax) {
				asm.push(GPR.EAX);
			}

			n.load();

			GPR rst = ((GPRLocation) n.getLocation()).getGPR();
			// TODO: Hack
			asm.move(GPR.EAX, elemSize);
			asm.imul(GPR.EAX, rst);

			asm.push(GPR.EAX);
			callInternal(gcalloc);

			stack.push(new RefItem(new GPRLocation(GPR.EAX)));

			asm.move(GPR.EAX, 0, rst);

			n.release();

			if (veax) {
				asm.pop(GPR.EAX);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_nop()
	 */
	@Override
	public void visit_nop() {
		// Doesnt anything.
		// Doesnt needed to emit NOP x86 instructions.
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_pop()
	 */
	@Override
	public void visit_pop() {
		genericPop(4);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_pop2()
	 */
	@Override
	public void visit_pop2() {
		genericPop(8);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_putfield(icebird.metadata.constpool.ConstField)
	 */
	@Override
	public void visit_putfield(ConstField fieldRef) {
		// TODO Auto-generated method stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_putstatic(icebird.metadata.constpool.ConstField)
	 */
	@Override
	public void visit_putstatic(ConstField fieldRef) {
		StaticField field = (StaticField) fieldRef.resolve();

		extern(field);

		if (field.isWide()) {
			DoubleWordItem item = stack.popDoubleWord();
			item.load();
			
			GPR2Location loc = ((GPR2Location)item.getLocation());
			asm.move(new Label(field.getMangledName()), 0, loc.getLW());
			asm.move(new Label(field.getMangledName()), 4, loc.getHW());
			
			item.release();
		} else {
			WordItem item = stack.popWord();
			
			GPR gpr = ((GPRLocation)item.getLocation()).getGPR();
			asm.move(new Label(field.getMangledName()), 0, gpr);

			item.release();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_ret(int)
	 */
	@Override
	public void visit_ret(int index) {
		wload(BasicType.Int, index);

		IntegerItem i = stack.popInteger();
		GPR gpr = ((GPRLocation) i.getLocation()).getGPR();

		i.release();

		asm.jmp(gpr, 0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_return()
	 */
	@Override
	public void visit_return() {
		int count = 0;

		while (!stack.isEmpty()) {
			Item v = stack.pop();

			if (v.getLocation().isStack()) {
				count += v.getCategory() * 4;
			}

			v.release();
		}

		if (count != 0) {
			asm.lea(X86Register.ESP, X86Register.ESP, count);
		}

		stackFrame.emitReturn();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_saload()
	 */
	@Override
	public void visit_saload() {
		// TODO Auto-generated method stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_sastore()
	 */
	@Override
	public void visit_sastore() {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_swap()
	 */
	@Override
	public void visit_swap() {
		Item v0 = stack.pop();
		Item v1 = stack.pop();

		if (v0.getCategory() != v1.getCategory()) {
			throw new RuntimeException("Wrong category");
		}

		boolean vn0 = (v0.getLocation().isFPUStack() || v0.getLocation()
				.isStack());
		boolean vn1 = (v1.getLocation().isFPUStack() || v1.getLocation()
				.isStack());

		if (vn0) {
			v0.load();
		} else if (vn1) {
			v1.load();
		}

		// Just swap.
		stack.push(v0);
		stack.push(v1);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see icebird.compiler.bytecode.BytecodeVisitor#visit_tableswitch(int,
	 *      int, int, int[])
	 */
	@Override
	public void visit_tableswitch(int defValue, int lowValue, int highValue,
			int[] addresses) {
		IntegerItem i = stack.popInteger();

		if (i.isPureVirtual()) {
			int value = ((IntegerConstantLocation) i.getLocation()).getValue();
			Label l = null;

			if (value < lowValue || value > highValue) {
				l = new Label(formatPC(defValue));
			} else {
				for (int j = 0; j < addresses.length; j++) {
					if (j == value) {
						l = new Label(formatPC(addresses[j]));
					}
				}

				if (l == null) {
					l = new Label(formatPC(defValue));
				}
			}

			asm.jmp(l);
			return;
		} else {
			Label def = new Label(formatPC(defValue));

			i.load();
			GPR gpr = ((GPRLocation) i.getLocation()).getGPR();

			asm.cmp(gpr, lowValue);
			asm.jcc(def, X86Constants.JL);
			asm.cmp(gpr, highValue);
			asm.jcc(def, X86Constants.JG);
			for (int j = 0; j < addresses.length; j++) {
				asm.cmp(gpr, j);
				asm.jcc(new Label(formatPC(addresses[j])), X86Constants.JE);
			}

			i.release();
		}
	}

	private void genericPop(int size) {
		Item v = stack.pop();

		if (v.getLocation().isStack()) {
			asm.lea(X86Register.SP, X86Register.SP, 4);
		}
		v.release();
	}

	private void callMethod(Method m) {
		Label callLabel = new Label(m.getMangledName());
		asm.call(callLabel);
		pushRetValue(m);
	}

	private void pushRetValue(Method m) {
		if (!m.returns())
			return;

		BasicType t = m.getReturnType();

		// Double or Long
		if (t.isWide()) {
			GPR l = (GPR)pool.request(GPR.EAX);
			GPR h = (GPR)pool.request(GPR.EBX);
			stack.push(DoubleWordItem.getGPRWord(l, h, t));
		} else {
			GPR r = (GPR)pool.request(GPR.EAX);
			stack.push(WordItem.getGPRWord(r, t));
		}
	}

	// Drop parameters
	private void dropParameters(Method m, boolean hasSelf) {
		BasicType[] argTypes = m.getArguments();
		int count = argTypes.length;

		for (int i = count - 1; i >= 0; i--) {
			BasicType arg = argTypes[i];
			Item tp = stack.pop();

			if (tp.getType() != arg) {
				// WARNING
			}

			tp.release();
		}

		if (hasSelf) {
			// Release self var.
			stack.pop().release();
		}
	}

	private void dwreturn(BasicType t, boolean vret) {
		DoubleWordItem w = stack.popDoubleWord();

		if (!(w.uses(GPR.EAX) && w.uses(GPR.EBX))) {
			w.loadTo(GPR.EAX, GPR.EBX);
		}

		// Is this needed?
		w.release();

		if (vret) {
			visit_return();
		}
	}

	private void wreturn(BasicType t, boolean vret) {
		WordItem w = stack.popWord();

		if (!w.uses(X86Register.EAX)) {
			w.loadTo(X86Register.EAX);
		}

		// Is this needed?
		//w.release();

		if (vret) {
			visit_return();
		}
	}
	
	private final void waload(BasicType t) {
		IntegerItem idx = stack.popInteger();
		RefItem ref = stack.popRef();
		
		GPR rgp = request();
		WordItem result = WordItem.getGPRWord(
			rgp, t
		);
		
		if (!idx.getLocation().isConstant()) {
			idx.load();
		}
		
		ref.load();
		GPR refr = ((GPRLocation)ref.getLocation()).getGPR();
		
		checkbound(ref, idx);
		
		if (t == BasicType.Char) {
			asm.xor(rgp, rgp);
		}
		
		if (idx.getLocation().isConstant()) {
			int offset = ((IntegerConstantLocation)idx.getLocation()).getValue() * t.getSize();
			asm.move(rgp, refr, offset + 8); // TODO: ARRAY DATA OFFSET
		} else {
			GPR idxr = ((GPRLocation)ref.getLocation()).getGPR();
			
			asm.move(rgp, refr, idxr, t.getSize(), 8);
		}
		
		if (t == BasicType.Byte) {
			asm.movesx(rgp, rgp);
		} else if (t == BasicType.Short) {
			asm.movesx(rgp, rgp);
		}
		
		idx.release();
		ref.release();
		
		stack.push(result);
	}

	private final int getShiftForMultiplier(int val) {
		int mul = 2;
		for (int i = 1; i <= 31; i++) {
			if (val == mul) {
				return i;
			}
			mul <<= 1;
		}

		// No applicable shift count found...
		return -1;
	}

	private int getOffset(int i) {
		int argc = source.getArguments().length;
		boolean isArg = argc > i;

		if (isArg) {
			return ((argc - i + 1) * 4) + 4;
		} else {
			return -((i + 1) * 4);
		}
	}

	private void dwstore(BasicType t, int index) {
		int offset = getOffset(index);

		stack.loadLocal(offset);

		DoubleWordItem item = (DoubleWordItem) stack.pop(t);
		boolean vconst = item.getLocation().isConstant();

		if (vconst) {
			constLocals.put(index, item);
		} else {
			// not constant any more.
			constLocals.remove(index);
		}

		if (vconst) {
			long val;
			if (item.getType() == BasicType.Double) {
				val = Double.doubleToRawLongBits(((DoubleConstantLocation) item
						.getLocation()).getValue());
			} else {
				val = ((LongConstantLocation) item.getLocation()).getValue();
			}

			asm.move_const(GPR.EBP, offset, (int) (val & 0xFFFFFFFFL));
			asm.move_const(GPR.EBP, offset - 4,
					(int) ((val >>> 32) & 0xFFFFFFFFL));
		} else if (item.getLocation().isFPUStack()) {
			FPUStack.fxch(stack.getFPUStack(), item);

			if (item.getType() == BasicType.Double) {
				asm.fstp64(GPR.EBP, offset);
			} else {
				asm.fistp64(GPR.EBP, offset);
			}

			stack.getFPUStack().pop();
		} else if (item.getLocation().isStack()) {
			asm.pop(GPR.EBP, offset);
			asm.pop(GPR.EBP, offset - 4);
		} else {
			item.load();
			GPR2Location loc = ((GPR2Location) item.getLocation());

			GPR lsb = loc.getLW();
			GPR msb = loc.getHW();

			asm.move(GPR.EBP, offset, lsb);
			asm.move(GPR.EBP, offset - 4, msb);
		}

		item.release();
	}

	private void wstore(BasicType t, int index) {
		int offset = getOffset(index);

		stack.loadLocal(offset);

		WordItem item = (WordItem) stack.pop();
		boolean vconst = item.getLocation().isConstant();

		if (vconst) {
			constLocals.put(index, item);
		} else {
			// not constant any more.
			constLocals.remove(index);
		}

		if (vconst) {
			if (item.getType() == BasicType.Float) {
				int v = Float.floatToRawIntBits(((FloatConstantLocation) item
						.getLocation()).getValue());
				asm.move_const(X86Register.EBP, offset, v);
			} else {
				// Store register
				// TODO: Make basic block optimization
				int v = ((IntegerConstantLocation) item.getLocation())
						.getValue();
				asm.move_const(X86Register.EBP, offset, v);
			}
		} else {
			Location loc = item.getLocation();

			if (loc.isFPUStack()) {
				FPUStack.fxch(stack.getFPUStack(), item);
				if (item.getType() == BasicType.Float) {
					asm.fstp(X86Register.EBP, offset);
				} else {
					asm.fistp(X86Register.EBP, offset);
				}
			} else if (loc.isStack()) {
				asm.pop(X86Register.EBP, offset);
			} else {
				item.load();
				asm.move(X86Register.EBP, offset, ((GPRLocation) loc).getGPR());
			}
		}

		item.release();
	}

	private void wload(BasicType t, int index) {
		Item cl = constLocals.get(index);

		if (cl != null) {
			stack.push(cl.clone());
		} else {
			stack.push(WordItem.getLocal(getOffset(index), t));
		}
	}

	private void dwload(BasicType t, int index) {
		Item cl = constLocals.get(index);

		if (cl != null) {
			stack.push(cl.clone());
		} else {
			stack.push(DoubleWordItem.getLocal(getOffset(index), t));
		}
	}

	private boolean isConstant(Item v1) {
		return v1.getLocation().isConstant();
	}

	private boolean isConstant(Item v1, Item v2) {
		return v1.getLocation().isConstant() && v2.getLocation().isConstant();
	}

	// Prepare operands to operation.
	private boolean prepare(Item dst, Item src, boolean com) {
		if (com) {
			if (dst.getLocation().getWeight() < dst.getLocation().getWeight()) {
				src.load(); // Load source

				if (dst.getLocation().isStack()
						|| dst.getLocation().isFPUStack()) {
					dst.load();
				}

				return true;
			}
		}

		if (src.getLocation().isStack() || src.getLocation().isFPUStack()) {
			src.load();
		}

		dst.load();

		return false;
	}

	private void lop(int lsbop, int msbop, boolean com) {
		LongItem v1 = stack.popLong();
		LongItem v2 = stack.popLong();

		if (prepare(v1, v2, com)) {
			// (Un)Swap
			LongItem tmp = v2;
			v1 = v2;
			v2 = tmp;
		}

		GPR lsb = ((GPR2Location) v1.getLocation()).getLW();
		GPR msb = ((GPR2Location) v1.getLocation()).getHW();

		if (v2.getLocation().isGPR()) {
			asm.arithOp(lsbop, lsb, ((GPR2Location) v2.getLocation()).getLW());
			asm.arithOp(msbop, msb, ((GPR2Location) v2.getLocation()).getHW());
		}

		if (v2.getLocation().isLocal()) {
			int offset = ((LocalLocation) v2.getLocation()).getOffset();

			asm.arithOp(lsbop, lsb, GPR.EBP, offset);
			asm.arithOp(msbop, msb, GPR.EBP, offset - 4);
		}

		if (v2.getLocation().isConstant()) {
			LongConstantLocation v = ((LongConstantLocation) v2.getLocation());

			asm.arithOp(lsbop, lsb, v.getLsbValue());
			asm.arithOp(msbop, msb, v.getMsbValue());
		}

		v2.release();
		stack.push(v1);
	}

	private void iop(int op, boolean com) {
		IntegerItem v2 = stack.popInteger();
		IntegerItem v1 = stack.popInteger();

		if (isConstant(v1, v2)) {
			int vv1 = ((IntegerConstantLocation) v1.getLocation()).getValue();
			int vv2 = ((IntegerConstantLocation) v2.getLocation()).getValue();

			switch (op) {
			case X86Operation.ADD:
				visit_iconst(vv1 + vv2);
				break;

			case X86Operation.SUB:
				visit_iconst(vv1 - vv2);
				break;

			case X86Operation.AND:
				visit_iconst(vv1 & vv2);
				break;

			case X86Operation.OR:
				visit_iconst(vv1 | vv2);
				break;

			case X86Operation.XOR:
				visit_iconst(vv1 ^ vv2);
				break;

			default:
				throw new IllegalArgumentException();
			}
		} else {
			if (prepare(v1, v2, com)) {
				// (Un)Swap
				IntegerItem tmp = v2;
				v1 = v2;
				v2 = tmp;
			}

			GPR r1 = ((GPRLocation) v1.getLocation()).getGPR();
			if (v2.getLocation().isGPR()) {
				asm.arithOp(op, r1, ((GPRLocation) v2.getLocation()).getGPR());
			}

			if (v2.getLocation().isLocal()) {
				asm.arithOp(op, r1, X86Register.EBP, ((LocalLocation) v2
						.getLocation()).getOffset());
			}

			if (v2.getLocation().isConstant()) {
				asm
						.arithOp(op, r1, ((IntegerConstantLocation) v2
								.getLocation()).getValue());
			}

			v2.release();
			stack.push(v1);
		}
	}

	// Runtime null-cheking.
	private void checknull(RefItem ref) {
		if (source.isUnsafe()) {
			// Unsafe method - dont check.
			return;
		}

		Location l = ref.getLocation();
		if (l.isConstant()) {
			System.out.println("null error can be.");
		}

		if (l.isGPR()) {
			//GPR gpr = ((GPRLocation) l).getGPR();

			// TODO: null checking.
		}
	}

	// Runtime bound-checking.
	private void checkbound(RefItem array, IntegerItem i) {
		if (source.isUnsafe()) {
			// Unsafe method - dont check.
			return;
		}
	}

	// Format PC into native label.
	private static String formatPC(int pc) {
		return ".JL_" + Integer.valueOf(pc);
	}

	// Call internal function.
	// This is just call. All handling by callee.
	private void callInternal(Label l) {
		// Just call function.
		asm.call(l);
	}

	// Extern public method.
	private void extern(Method m) {
		extern(new Label(m.getMangledName()));
	}

	// Extern public static field.
	private void extern(Field f) {
		extern(new Label(f.getMangledName()));
	}

	// Extern public label.
	private void extern(Label l) {
		if (exists(l))
			return;

		extrns.add(l);
	}

	// Checks, is specified field is already
	// externed.
	private boolean exists(Label l) {
		for (Label lb : extrns) {
			if (lb.equals(l))
				return true;
		}

		return false;
	}

	private static Label gcalloc = new Label("gcalloc");
}
